-- Está mal aun Matías... es un ralle...


-- Matías Blanco & Sergio Pérez
-- Hippies - Inventadas

with Ada.Text_Io ; use Ada.Text_Io;
with Ada.Integer_Text_Io; use Ada.Integer_Text_Io;
with Ada.Numerics.Discrete_Random;

procedure HippiesCitasAda is

   -- Variables y demás
   MAX_HIPPIES: constant Integer := 10;

   -- Aleatorio discreto

   subtype valores is Integer range 1..MAX_HIPPIES;
   package NumeroAleatorio is new Ada.Numerics.Discrete_Random(valores);

   use NumeroAleatorio;

   F: Generator;

   Aux1: Integer;
   Aux2: Integer;

   FIN_JUEGO: Boolean:= False;

   type THuevos is array (1..MAX_HIPPIES) of Boolean;

   task HippieSabio is -- Este sabe todo lo que hay :)
      entry Tengo(Id: in Integer);
      entry No_Tengo(Id: in Integer);
      entry Tiene(Id: in Integer; OK_Tiene: in out Boolean);
      entry EstoyEliminado(Id: in Integer);
      entry EstaEliminado(Id: in Integer; OK_ELIMINADO: in out Boolean);
      entry N_Elim(N: in out Integer);
      entry SubitaLanzada;
      entry IS_Subita(L: in out Boolean);
   end HippieSabio;

   task body HippieSabio is
      Hippie_Tiene_Huevo: THuevos := (others => False);
      Hippie_Eliminado: THuevos:= (others => False);
      Jugando: Integer:= MAX_HIPPIES;
      Subita: Boolean := False;
   begin
      while (not FIN_JUEGO) Loop
         select
            accept Tengo(Id: in Integer) do
               Hippie_Tiene_Huevo(Id):= True;
            end Tengo;
         or
            accept No_Tengo(Id: in Integer) do
               Hippie_Tiene_Huevo(Id):= False;
            end No_Tengo;
         or
            accept Tiene(Id: in Integer; OK_Tiene: in out Boolean) do
               OK_Tiene := Hippie_Tiene_Huevo(Id);
            end Tiene;
         or
            accept EstoyEliminado(Id: in Integer) do
               Hippie_Eliminado(Id):= True;
            end EstoyEliminado;
         or
            accept EstaEliminado(Id: in Integer; OK_ELIMINADO: in out Boolean) do
               OK_ELIMINADO := Hippie_Eliminado(Id);
            end EstaEliminado;
         or
            accept N_Elim(N: in out Integer) do
               Jugando := 0;
               for I in 1..MAX_HIPPIES loop
                  if (not Hippie_Eliminado(I)) then
                     Jugando:= Jugando + 1;
                  end if;
               end loop;
               N:= Jugando;
            end N_Elim;
         or
            accept SubitaLanzada do
               Subita := True;
            end SubitaLanzada;
         or
            accept Is_Subita(L: in out Boolean) do
               L := Subita;
            end Is_Subita;
         end select;
      end loop;

   end HippieSabio;


   task MembranaCentro is
      entry Tocar(Id: in Integer);
      entry Soltar(Id: in Integer);
   end MembranaCentro;

   task body MembranaCentro is
      Tocada: Boolean := False;
   begin
      while (not FIN_JUEGO) Loop
         select
            when (not Tocada) =>
               accept Tocar(Id: in Integer) do
                  Put_Line("El hippie " & Integer'Image(Id) & " toca la mebrana");
                  Tocada:= True;
               end Tocar;
         or
            accept Soltar(Id: in Integer) do
               Put_Line("El hippie " & Integer'Image(Id) & " suelta la mebrana");
               Tocada := False;
            end Soltar;
         end select;
      end loop;
   end MembranaCentro;

   task CestaHuevos is
      entry CogerHuevo(Id: in Integer);
   end CestaHuevos;

   task body CestaHuevos is
   begin
      while (not FIN_JUEGO) loop
        accept CogerHuevo(Id: in Integer) do
           Put_Line("El hippie " & Integer'Image(Id) & " esta cogiendo un huevo");
           delay (1.0);
           Put_Line("El hippie " & Integer'Image(Id) & " consigue un huevo");
        end CogerHuevo;
      end loop;
   end CestaHuevos;

   task type Hippie is
      entry Init(Idd: in Integer; N: in Integer);
      entry Lanzar_Hacia (Idd: in Integer; OK: in out Boolean);
      entry MuerteSubita (Idd: in Integer; OK: in out Boolean);
   end Hippie;

   Hippies: array (1..MAX_HIPPIES) of Hippie;

   task body Hippie is
      Id: Integer;
      Huevos: Integer;
      Jugando: Boolean := True;
      Aux_Random: Integer;
      H_Tiene_Otro: Boolean;

      IS_OK: Boolean := False;

      EliminadoManda: Boolean := False;

      N_Jugando: Integer:= MAX_HIPPIES;

      DebeHuevo: Boolean;


   begin
      accept Init(Idd: in Integer; N: in Integer) do
         if (N = 1) then
            HippieSabio.Tengo(Idd);
         end if;
         Put_Line("El hippie " & Integer'Image(Idd) & " empieza con" & Integer'Image(N) & " huevo/s");
         Huevos :=N;
         Id:= Idd;
      end Init;

      delay (1.0);



      while (N_Jugando /= 1) loop


         H_Tiene_Otro := True;

--         Put("P:" & Integer'Image(Id) );

         HippieSabio.N_Elim(N_Jugando);

         Put_Line("NJUGANDO " & Integer'Image(N_Jugando));

         if (N_Jugando = 1 and Jugando) then
            Put_Line("El hippie " & Integer'Image(Id) & " ES EL GANADOR!");
            FIN_JUEGO := TRUE;

         elsif (N_Jugando = 2) then
            Put_Line("El hippie " & Integer'Image(Id) & " SABE QUE QUEDAN 2!!");
            for I in 1..MAX_HIPPIES loop
                  HippieSabio.EstaEliminado(I, EliminadoManda);
                  if (I /= Id and not EliminadoManda) then
                     Put_Line("El hippie " & Integer'Image(Id) & " LANZA MUERTE SUBITA");
                     HippieSabio.IS_Subita(IS_OK);
                     if (not IS_OK) then
                        HippieSabio.SubitaLanzada;
                        Huevos := 0;
                        Hippies(I).MuerteSubita(Id, IS_OK);
                     end if;
                  end if;
                  if (not IS_OK) then
                     Jugando := False;
                  end if;
            end loop;
         else
            if (Huevos > 0 and Jugando) then -- Si tiene un huevo y si no esta eliminado
               Put_Line("El hippie " &Integer'Image(Id) & " necesita tocar la membrana");
--               MembranaCentro.Tocar(Id);
               EliminadoManda := False;
               H_Tiene_Otro := True;
               Aux_Random := 0;
               while ((H_Tiene_Otro or EliminadoManda) or (Aux_Random = Id)) loop
                  Aux_Random:= Random(F);
                  HippieSabio.Tiene(Aux_Random, H_Tiene_Otro);
                  HippieSabio.EstaEliminado(Aux_Random, EliminadoManda);
                  Put("*");
               end loop;
               -- Uno que no tiene y juega .. a lanzar.
               Put_Line("El hippie " &Integer'Image(Id) & " se lo quiere pasar al " & Integer'Image(Aux_Random));
               delay (1.0); -- Tarda un poco en apuntar y lanzar
               Huevos := 0; -- Lo lanza
               Hippies(Aux_Random).Lanzar_Hacia(Id, IS_OK);
 --              MembranaCentro.Soltar(Id); -- Suelta la membrana
               if (not IS_OK) then
                  Jugando := False; -- Eliminado!
                  HippieSabio.EstoyEliminado(Id); -- Se lo digo al sabio, que lo sepa
                  Put_Line("El hippie " &Integer'Image(Id) & " se elimina al mandar a alguien que no podia");
                  CestaHuevos.CogerHuevo(Id); -- Coge otro huevo
                                              -- Busca uno
                  while (H_Tiene_Otro and not EliminadoManda) loop
                     Aux_Random:= NumeroAleatorio.Random(F);
                     HippieSabio.Tiene(Aux_Random, H_Tiene_Otro);
                     HippieSabio.EstaEliminado(Aux_Random, EliminadoManda);
                     Put("-");
                  end loop;
                  -- ... y se lo pasa inmediatamente sin retraso
                  Hippies(Aux_Random).Lanzar_Hacia(Id, IS_OK); -- Lo pone en juego
               end if;
               HippieSabio.No_Tengo(Id); -- Ya no tiene huevo
            end if;
         end if;
--         while (Huevos = 0 and not FIN_JUEGO)  loop
--            Put_Line("."&Integer'Image(Id) & Integer'Image(Huevos) & "<-");
            select
               accept Lanzar_Hacia(Idd: in Integer; OK: in out Boolean) do
                  -- Este accept recibe un huevo
                  if (Jugando) then
                     if (Huevos = 1) then
                        OK:= False;
                        Put_Line("El hippie " &Integer'Image(Id) & " recibe un huevo de" & Integer'Image(Idd) & "PERO YA TENIA!");
                     else
                        Put_Line("El hippie " &Integer'Image(Id) & " recibe un huevo " & Integer'Image(Idd));
                        OK:= True;
                        if (NumeroAleatorio.Random(F) < 3) then
                           Put_Line("El hippie " &Integer'Image(Id) & " ELIMINADO. Ha roto el huevo de " & Integer'Image(Idd));
                           Jugando:= False;
                           HippieSabio.EstoyEliminado(Id);
                           OK := True;
                           DebeHuevo := True;
                        else
                           DebeHuevo:= False;
                           Huevos:= 1;
                        end if;
                     end if;
                  else
                     Put_Line("El hippie " &Integer'Image(Id) & " recibe un huevo y YA NO JUGABA: Eliminado -> " & Integer'Image(Idd));
                     OK:= False; -- Si no juega, el que lanza es eliminado
                  end if;
               end Lanzar_Hacia;
--               Aux_Random:=0;
--               EliminadoManda := False;
--               if DebeHuevo then
--                  Put("DEBE");
--                  while (EliminadoManda) loop
                     Aux_Random:= Random(F);
--                     Put("RAND-" & Integer'Image(Aux_Random));
--                     HippieSabio.Tiene(Aux_Random, H_Tiene_Otro);
--                     HippieSabio.EstaEliminado(Aux_Random, EliminadoManda);
--                  end loop;
                  -- ... y se lo pasa inmediatamente sin retraso
                  Put_Line("El hippie " &Integer'Image(Id) & " le manda un huevo de reserva al " & Integer'Image(Aux_Random));
                  --Hippies(Aux_Random).Lanzar_Hacia(Id, IS_OK); -- Lo pone en juego
--               end if;
            or
               accept MuerteSubita(Idd : in Integer; OK: in out Boolean) do
                  Put_Line("El hippie " &Integer'Image(Id) & " recibe una muerte subita desde " & Integer'Image(Idd));
                  if (Random(F) < (MAX_HIPPIES)/2)then
                     Put_Line("El hippie " &Integer'Image(Id) & " es EL GANADOR porque ESQUIVA LA MUERTE SUBITA DE " & Integer'Image(Idd));
                     OK:= False;
                  else
                     Put_Line("El hippie " &Integer'Image(Id) & " es ELIMINADO DE MUERTE SUBITA por " & Integer'Image(Idd) & " QUE GANAAAA!!!");

                     OK := True;
                     Jugando := False;
                     HippieSabio.EstoyEliminado(Id);
                  end if;
                  FIN_JUEGO := TRUE;
               end MuerteSubita;
            or
               delay 1.0;

            end select;
--         end loop;
--            Put("==");
      end loop;
   end Hippie;




begin

   while (True) loop
      Put_Line("Matias, no funciona aun");
      delay 1.0;
   end loop;


   NumeroAleatorio.Reset(F);
   ------------------------------------------------
   -- Necesitamos dos numeros aleatorios diferentes
   -- que seran los que empiecen con un huevo :)
   Aux1 := Random(F);
   Aux2 := Random(F);
   while (Aux1 = Aux2) loop
      Aux2 := NumeroAleatorio.Random(F);
   end loop;
   ------------------------------------------------

   for I in 1..MAX_HIPPIES loop
      if (I = Aux1 or I = Aux2) then
         Hippies(I).Init(I, 1);
      else
         Hippies(I).Init(I, 0);
      end if;
   end loop;




end HippiesCitasAda;

